Перейти к основному содержимому

5.08. Справочник по Smalltalk

Разработчику Архитектору

Справочник по Smalltalk

1. Общие принципы языка

Smalltalk — это чисто объектно-ориентированный язык программирования, в котором всё является объектом. Каждое значение, каждая операция, каждый тип данных выражается через объекты и сообщения. Язык был разработан в 1970-х годах в исследовательском центре Xerox PARC как часть интегрированной среды разработки и обучения.

Smalltalk отличается минимальным синтаксисом, полной интерактивностью и мощной интроспекцией. Программы пишутся не в виде текстовых файлов, а в виде живых объектов, хранящихся в образе (image), который содержит всё состояние системы: код, данные, окружение, историю.


2. Основные концепции

2.1. Всё — объект

  • Числа — объекты класса Integer, Float и подклассов.
  • Символы — объекты класса Symbol.
  • Строки — объекты класса String.
  • Булевы значения — объекты класса True и False.
  • Классы — объекты класса Class.
  • Методы — объекты класса CompiledMethod.
  • Даже nil — объект класса UndefinedObject.

2.2. Сообщения вместо вызовов

Взаимодействие между объектами происходит через отправку сообщений. Существует три типа сообщений:

2.2.1. Унарные сообщения

Сообщения без аргументов.
Пример:

3 factorial
Date today
'hello' reversed

2.2.2. Бинарные сообщения

Сообщения с одним аргументом, использующие специальные символы (+, -, =, >, //, @ и др.).
Пример:

5 + 3
a > b
point @ 10

2.2.3. Ключевые сообщения

Сообщения с именованными аргументами, где каждое ключевое слово заканчивается двоеточием.
Пример:

Array with: 1 with: 2 with: 3
Dictionary new at: 'key' put: 'value'
Point x: 10 y: 20

2.3. Приоритет сообщений

Порядок вычисления определяется строго:

  1. Унарные сообщения (слева направо)
  2. Бинарные сообщения (слева направо)
  3. Ключевые сообщения (слева направо)

Скобки () могут изменять порядок явно.

Пример:

3 + 4 factorial  "означает 3 + (4 factorial) = 3 + 24 = 27"
(3 + 4) factorial "означает 7 factorial = 5040"

3. Синтаксис

3.1. Литералы

ТипПримерОписание
Целые числа42Объект класса Integer
Вещественные3.14Объект класса Float
Строки'Hello'Объект класса String
Символы#helloОбъект класса Symbol
Массивы#(1 2 3)Литерал массива из литералов
Динамические массивы{1. 2. 3}Вычисляемый массив (в некоторых реализациях)
Блоки`[ :xx + 1 ]`
Хэш-литералы#{'a' -> 1. 'b' -> 2}Литерал словаря (в Pharo/Squeak)

3.2. Переменные

3.2.1. Виды переменных

  • Временные переменные: объявляются в методе через | a b c |
  • Параметры блока: указываются после открывающей скобки блока, например [ :x :y | x + y ]
  • Переменные экземпляра: принадлежат объекту, объявляются в классе
  • Классовые переменные: общие для всех экземпляров класса
  • Глобальные переменные: начинаются с заглавной буквы, например Transcript, Smalltalk
  • Пул переменных: совместно используемые переменные, объявленные в отдельном пространстве

3.2.2. Объявление временных переменных

| a b result |
a := 10.
b := 20.
result := a + b.
^ result

3.3. Присваивание

Оператор присваивания — :=.
Пример:

x := 5.
name := 'Alice'.

Множественное присваивание возможно:

a := b := c := 0.

3.4. Возврат значения

Оператор ^ возвращает значение из метода или блока.
Если ^ не используется, метод возвращает self.

Пример:

double: x
^ x * 2

4. Блоки (замыкания)

Блоки — это объекты, представляющие исполняемый код. Они могут захватывать окружающий контекст.

4.1. Синтаксис

[ :arg1 :arg2 | выражение ]

Примеры:

[ 42 ]                          "блок без аргументов"
[ :x | x * x ] "блок с одним аргументом"
[ :a :b | a max: b ] "блок с двумя аргументами"

4.2. Выполнение блока

Блоки выполняются через унарные сообщения:

  • value — без аргументов
  • value: — с одним аргументом
  • value:value: — с двумя и т.д.

Пример:

increment := [ :x | x + 1 ].
result := increment value: 5. "результат — 6"

4.3. Использование в коллекциях

Блоки активно используются для итерации:

#(1 2 3 4) do: [ :each | Transcript show: each printString; cr ].
#(1 2 3 4) collect: [ :x | x * 2 ] "→ #(2 4 6 8)"

5. Классы и объекты

5.1. Создание класса

В среде Smalltalk классы создаются через браузер классов. Синтаксически определение класса выглядит так:

Object subclass: #MyClass
instanceVariableNames: 'name age'
classVariableNames: 'DefaultAge'
package: 'MyPackage'

5.2. Методы

Методы определяются в контексте класса. Синтаксис:

methodName
"комментарий"
| tempVar |
tempVar := self computeSomething.
^ tempVar

С параметрами:

setName: aString age: anInteger
name := aString.
age := anInteger

5.3. Конструкторы

Классовые методы часто служат фабриками:

MyClass class >> newWithName: aString
^ self new
setName: aString;
yourself

5.4. Наследование

Smalltalk поддерживает одиночное наследование. Все классы наследуются от Object, кроме ProtoObject.

Цепочка наследования:

ProtoObject → Object → Collection → SequenceableCollection → Array → ...

5.5. Метаклассы

Каждый класс — это объект, и у него есть собственный класс — метакласс. Метаклассы содержат методы, применимые к самому классу (например, new, initialize).


6. Коллекции

Smalltalk предоставляет богатую иерархию коллекций.

6.1. Основные типы

КлассОписание
ArrayИндексируемая последовательность фиксированной длины
OrderedCollectionДинамический массив
SetКоллекция без дубликатов
DictionaryХэш-таблица (ключ-значение)
BagКоллекция с подсчётом вхождений
IntervalДиапазон чисел (например, 1 to: 10)
StringПоследовательность символов
ByteArrayПоследовательность байтов

6.2. Общие методы коллекций

  • do: aBlock — итерация
  • collect: aBlock — преобразование
  • select: aBlock — фильтрация по условию
  • reject: aBlock — фильтрация с инверсией
  • anySatisfy: aBlock — проверка существования
  • allSatisfy: aBlock — проверка универсальности
  • size — количество элементов
  • isEmpty — проверка на пустоту
  • includes: anObject — проверка наличия
  • at: index — доступ по индексу
  • at: index put: value — запись по индексу

Пример:

numbers := #(1 2 3 4 5).
evens := numbers select: [ :x | x even ].
squares := numbers collect: [ :x | x * x ].

7. Исключения и обработка ошибок

Smalltalk использует механизм исключений на основе блоков.

7.1. Базовый синтаксис

[ riskyCode ] on: Error do: [ :ex | handleException ]

7.2. Типы исключений

  • Error — общая ошибка
  • MessageNotUnderstood — отправка неизвестного сообщения
  • SubscriptOutOfBounds — выход за границы массива
  • ZeroDivide — деление на ноль

7.3. Возбуждение исключений

Error signal: 'Something went wrong'

7.4. Обеспечение и гарантированное выполнение

[ work ] ensure: [ cleanup ]  "cleanup выполнится всегда"

8. Среда выполнения и инструменты

8.1. Образ (Image)

Вся система Smalltalk хранится в одном файле образа (.image). Он содержит:

  • Все объекты
  • Все классы и методы
  • Все открытые окна и состояния
  • Историю работы

Загрузка образа — мгновенный запуск всей среды.

8.2. Инструменты

  • System Browser — просмотр и редактирование классов
  • Workspace — интерактивная область для выполнения кода
  • Transcript — системный лог
  • Debugger — отладчик с возможностью изменения кода "на лету"
  • Inspector — просмотр внутреннего состояния объекта
  • Versions Browser — история изменений методов

8.3. Интроспекция

Smalltalk позволяет исследовать себя во время выполнения:

Object allSubclasses          "все подклассы Object"
MyClass selectors "все методы класса"
method sourceCode "исходный код метода"
obj class "класс объекта"
obj respondsTo: #methodName "проверка поддержки сообщения"

9. Пакеты и модули

Smalltalk организует код в пакеты (Packages). Каждый класс принадлежит одному пакету. Пакеты:

  • Группируют связанные классы
  • Упрощают навигацию
  • Используются при экспорте/импорте кода
  • Поддерживают зависимости (в современных реализациях, например, Pharo)

10. Реализации Smalltalk

РеализацияОсобенности
SqueakОриентирована на образование, создана Alan Kay
PharoСовременная, активно развиваемая, основанная на Squeak
VisualWorksКоммерческая, мощная IDE, поддержка enterprise
GNU SmalltalkТекстовая, похожа на скриптовые языки
VA SmalltalkДля корпоративных решений, IBM-совместимость

11. Стандартная библиотека (Kernel и Collections)

11.1. Класс Object — корень иерархии

Все объекты наследуют базовые методы от Object:

  • class — возвращает класс объекта
  • isNil, notNil — проверка на nil
  • ifNil:, ifNotNil: — условное выполнение
  • yourself — возвращает self (полезно для каскадирования)
  • perform: aSymbol — динамическая отправка сообщения
  • perform:withArguments: — отправка с массивом аргументов
  • shallowCopy, deepCopy — копирование
  • hash, = — сравнение и хэширование
  • printString, storeString — текстовое представление

Пример каскадирования:

Array new
add: 1;
add: 2;
yourself

11.2. Числовые классы

  • Integer — целые числа (подклассы: SmallInteger, LargePositiveInteger, LargeNegativeInteger)
  • Float — числа с плавающей точкой (IEEE 754)
  • Fraction — рациональные числа (3/4)
  • Number — общий предок всех чисел

Методы:

  • + - * / // \\ — арифметика
  • abs, negated, reciprocal
  • even, odd
  • to:, to:by: — создание интервалов
  • factorial, sqrt, sin, cos

Особенность: деление / всегда возвращает Float или Fraction, а // — целочисленное деление.

11.3. Строки и символы

  • String — изменяемая последовательность символов
  • Symbol — неизменяемый, уникальный идентификатор

Методы строк:

  • size, isEmpty
  • at:, at:put:
  • copyFrom:to:
  • includesSubsequence:, findString:
  • asUppercase, asLowercase
  • lines — разбивает на строки
  • withCRs, withLFs — нормализация окончаний

Символы часто используются как ключи, селекторы методов, имена переменных.

11.4. Булевы значения

  • true — экземпляр класса True
  • false — экземпляр класса False

Методы:

  • ifTrue:, ifFalse:
  • ifTrue:ifFalse:
  • and:, or:, not

Пример:

(valid and: [ password size > 8 ]) ifTrue: [ self acceptLogin ]

Здесь and: принимает блок — ленивая оценка.


12. Графическая система: Morphic

Morphic — это каркас пользовательского интерфейса, используемый в Squeak и Pharo. Всё на экране — это морф (Morph), объект, умеющий рисовать себя и реагировать на события.

12.1. Основные морфы

МорфНазначение
MorphБазовый элемент UI
RectangleMorphПрямоугольник с цветом и границей
StringMorphОтображение текста
ImageMorphОтображение изображения
TextMorphРедактируемый многострочный текст
PluggableButtonMorphКнопка с действием
ScrollPaneПрокручиваемая область

12.2. Создание и отображение

m := RectangleMorph new.
m color: Color red.
m extent: 100@100.
m openInHand. "показать в центре курсора"

12.3. События

Морфы реагируют на:

  • клики (mouseDown:, mouseUp:)
  • перемещение мыши (mouseMove:)
  • клавиатуру (keyStroke:)
  • перетаскивание (startDrag:)

Можно переопределить методы или использовать on:send:to::

button on: #click send: #doSomething to: self

12.4. Компоновка

  • addMorph: — добавление дочернего морфа
  • layoutPolicy — стратегия размещения (например, TableLayout, LinearLayout)
  • hResizing, vResizing — поведение при изменении размера

13. Сериализация и сохранение

13.1. storeString и readFrom:

Любой объект может быть преобразован в строку, пригодную для повторного чтения:

obj := #(1 2 3).
text := obj storeString. "→ '#(1 2 3)'"
restored := Compiler evaluate: text.

Работает для большинства литералов и простых структур.

13.2. Файловый ввод-вывод

file := 'data.txt' asFileReference writeStream.
file nextPutAll: 'Hello'.
file close.

content := 'data.txt' asFileReference contents.

13.3. Образ как форма сериализации

Поскольку весь мир Smalltalk — это образ, сохранение состояния достигается простым сохранением .image-файла. Это включает:

  • Все объекты
  • Все открытые окна
  • Историю отладчика
  • Текущие вычисления

Это мощный механизм, но не подходит для обмена с внешними системами.

13.4. JSON и XML

Современные реализации (Pharo) поддерживают:

  • NeoJSONReader, NeoJSONWriter
  • XMLDOMParser

Пример:

json := NeoJSONObject new.
json at: 'name' put: 'Alice'.
jsonString := String streamContents: [ :s | (NeoJSONWriter on: s) write: json ].

14. Сетевые возможности

14.1. HTTP-клиент

Pharo предоставляет ZnClient (Zinc HTTP Components):

response := ZnClient new
url: 'https://api.example.com/data';
get.
data := response contents.

Поддержка:

  • GET, POST, PUT, DELETE
  • Заголовки
  • JSON-тело
  • Аутентификация

14.2. Сервер

Можно запустить встроенный HTTP-сервер:

server := ZnServer startOn: 8080.
server delegate: (ZnDefaultStaticServerDelegate directory: FileSystem workingDirectory).

Или создать REST API через маршрутизацию.

14.3. Сокеты

Низкоуровневый доступ через Socket:

socket := Socket newTCP.
socket connectTo: NetNameResolver loopbackAddress port: 8080.
socket sendData: 'PING'.
reply := socket receiveData.

15. Тестирование

Smalltalk включает встроенную систему модульного тестирования — SUnit (предтеча xUnit).

15.1. Написание теста

TestCase subclass: #MyClassTest
instanceVariableNames: ''
package: 'MyPackage-Tests'

MyClassTest >> testDouble
| result |
result := MyClass double: 5.
self assert: result equals: 10.

15.2. Утверждения

  • assert: aBoolean
  • assert: actual equals: expected
  • assert: aCollection includes: anElement
  • should: [ code ] raise: Error
  • shouldnt: [ code ] raise: Error

15.3. Подготовка и завершение

  • setUp — выполняется перед каждым тестом
  • tearDown — после каждого теста

Пример:

setUp
collection := OrderedCollection new.

testAdd
collection add: 42.
self assert: collection size equals: 1.

16. Внешние вызовы (FFI)

Smalltalk может вызывать нативные библиотеки через Foreign Function Interface.

Пример (Pharo + UFFI):

LibC class >> ffiLibrary
^ #('libc.so.6')

LibC class >> strlen: aString
<cdecl: long 'strlen' (char*) module: #('libc.so.6')>
^ self externalCallFailed

Использование:

len := LibC strlen: 'hello' asByteArray.

Требует осторожности: ошибки могут упасть всю систему.


17. Практические примеры

17.1. Калькулятор выражений

evaluate: expressionString
^ Compiler evaluate: expressionString

⚠️ Только в доверенной среде! Compiler evaluate: исполняет любой код.

17.2. Реализация стека

Stack := OrderedCollection.

push: item
self addLast: item.

pop
^ self removeLast.

isEmpty
^ self isEmpty.

17.3. Observer-паттерн

subject addDependent: observer.
subject changed.
"observer получит #update: от subject"

Smalltalk включает эту модель «из коробки» через Model и Dependents.


18. Настройки и параметры среды

18.1. Глобальные настройки

Доступны через Smalltalk preferences:

  • cmdKeysInText — использовать Cmd-комбинации в тексте
  • scrollBarsWithoutMenuButton — стиль прокрутки
  • annotationPane — показ аннотаций в браузере

18.2. Цвета и шрифты

StandardFonts defaultFont: (LogicalFont familyName: 'Source Code Pro' pointSize: 12).
Color blue muchDarker.

18.3. Логирование

Transcript show: 'Debug info'; cr.
Transcript clear.

Transcript — глобальный объект вывода, видимый в отдельном окне.


19. Производительность и оптимизация

  • Избегайте частого создания больших коллекций в циклах
  • Используйте becomeForward: осторожно — меняет идентичность объектов
  • #= и #hash должны быть согласованы для использования в Set/Dictionary
  • Блоки без захвата контекста компилируются эффективнее
  • SmallInteger операции — самые быстрые (встроены в VM)

20. Экосистема и сообщество

  • Pharo — активное сообщество, ежегодные конференции (Pharo Days)
  • GitHub — тысячи пакетов на github.com/pharo-project
  • Catalog — менеджер пакетов в Pharo (World menu → Tools → Catalog)
  • Pillar — система документирования (аналог Markdown + LaTeX)
  • Glamour — фреймворк для создания инструментов анализа кода